/**
 * Copyright 2010, 2011 Guenther Hoelzl, Shawn Brown
 *
 * This file is part of MINDdroid.
 *
 * MINDdroid is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * MINDdroid is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * MINDdroid. If not, see <http://www.gnu.org/licenses/>.
 **/

package com.evolved.automata.android.bots.speechbot;

import android.app.*;
import android.bluetooth.*;
import android.hardware.*;
import android.content.*;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.*;
import android.widget.AdapterView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import android.speech.RecognizerIntent;
import android.speech.tts.TextToSpeech;



import android.media.*;
import android.widget.*;
import android.content.pm.FeatureInfo;
import android.content.res.*;

/**
 * This class is for talking to a LEGO NXT robot and controlling it
 * via bluetooth and the built in acceleration sensor.
 * The communciation to the robot is done via LCP (LEGO communication protocol), 
 * so no special software has to be installed on the robot.
 */
public class SpeechBot extends Activity
{

	
    /********************************************************
     * Start of Bluetooth and Mindstorms related Globals 
     ********************************************************
     */
	
    public static final int UPDATE_TIME = 200;
    public static final int MENU_TOGGLE_CONNECT = Menu.FIRST;
    public static final int MENU_QUIT = Menu.FIRST + 1;
    
    private static final int REQUEST_CONNECT_DEVICE = 1000;
    private static final int REQUEST_ENABLE_BT = 2000;
    
    private NewBTCommunicator myBTCommunicator = null;
    
    private ProgressDialog connectingProgressDialog;
   
    private Activity thisActivity;
    
    private boolean debug=true;
    private Toast reusableToast;
    
    
    TextToSpeech speechEngine;
    final int DEVICE_CH0OSER_DIALOG=0;
    private String selectedMacAddress=null;
    
    int distance;
    int prevPower=0;
    LinkedBlockingQueue<BotMessage> messageQueue;
    RobotController rController;
    SpeechProcessor sProcessor;
    ArrayAdapter<String> speechResults;
    /********************************************************
     * End of Bluetooth and Mindstorms related Globals 
     ********************************************************
     */
    
    
    
	private static final int SPEECH_RECOGNIZER_READY=0;
	private static final int TEXT_TO_SPEECH_READY=1;
	int SPEECH_INTENT=0;
    
    /******************************
     * End of Balancing related Globals 
     ******************************
     */
    
	
	public static class BotMessage
	{
		public String data;
		public boolean cont=true;
		
		public BotMessage(String speech, boolean Continue)
		{
			cont=Continue;
			this.data=speech;
		}
		
	}

    /********************************************************
     * End of State-Machine related Globals 
     ********************************************************
     */
    
    /**
     * Receive messages from the NewBTCommunicator
     */
    final Handler myHandler = new Handler() {
        @Override
        public void handleMessage(Message myMessage) {
            switch (myMessage.getData().getInt("message")) {
                case NewBTCommunicator.DISPLAY_TOAST:
                    showToast(myMessage.getData().getString("toastText"), Toast.LENGTH_SHORT);
                    break;
                case NewBTCommunicator.STATE_CONNECTED:
                	myBTCommunicator.setSensorConfiguration();
                	Button startButton = (Button)findViewById(R.id.restartAction);
                	startButton.setEnabled(true);
                	processRobot();
                    break;
                case NewBTCommunicator.MOTOR_STATE:

                    if (myBTCommunicator != null) 
                    {
                        byte[] motorMessage = myBTCommunicator.getReturnMessage();
                        int position = byteToInt(motorMessage[21]) + (byteToInt(motorMessage[22]) << 8) + (byteToInt(motorMessage[23]) << 16)
                                       + (byteToInt(motorMessage[24]) << 24);
                        // TODO: Do something else here
                        showToast(getResources().getString(R.string.current_position) + position, Toast.LENGTH_SHORT);
                    }

                    break;
                case NewBTCommunicator.STATE_CONNECTERROR:
                    //connectingProgressDialog.dismiss();
                case NewBTCommunicator.STATE_RECEIVEERROR:
                case NewBTCommunicator.STATE_SENDERROR:
                	showToast("An error has occurred", Toast.LENGTH_SHORT);
                	AlertDialog.Builder alertBuilder = new AlertDialog.Builder(thisActivity);
                	alertBuilder.setMessage("There was an error with the connection to the remote device");
                	alertBuilder.setPositiveButton("Connect again", getBTReconnectRetryListener());
                	alertBuilder.setNeutralButton("Choose NXT again", getSelectNXTListener());
                	alertBuilder.setNegativeButton("Quit", getSelectExitListener());
                	alertBuilder.setTitle("Connection Error!");
                	alertBuilder.show();
                    break;
                case NewBTCommunicator.UPDATE_CENTER:
                	String batteryDisplay = myMessage.getData().getString("battery");
                	String sonicDisplay = myMessage.getData().getString("sonic");
                	String collisionDisplay = myMessage.getData().getString("bumper");
                	
    				TextView sonic = (TextView)findViewById(R.id.dataCenter);
    		        sonic.setText(sonicDisplay);
    				TextView switchState = (TextView)findViewById(R.id.data1);
    				switchState.setText(collisionDisplay);
    				TextView battery = (TextView)findViewById(R.id.data2);
    				battery.setText(batteryDisplay);
                	break;
                
            }
        }
    };


    
    private DialogInterface.OnClickListener getBTReconnectRetryListener()
    {
    	return new DialogInterface.OnClickListener()
    	{
    		@Override
			public void onClick(DialogInterface dialog, int which) 
			{
    			myBTCommunicator.onStart(selectedMacAddress);
				dialog.dismiss();
			}
    	};
    }
    
    private DialogInterface.OnClickListener getSelectNXTListener()
    {
    	return new DialogInterface.OnClickListener()
    	{
    		@Override
			public void onClick(DialogInterface dialog, int which) 
			{
    			selectNXT();
				dialog.dismiss();
			}
    	};
    }
    
    private DialogInterface.OnClickListener getSelectExitListener()
    {
    	return new DialogInterface.OnClickListener()
    	{
    		@Override
			public void onClick(DialogInterface dialog, int which) 
			{
    			finish();
				dialog.dismiss();
			}
    	};
    }
    
    public SpeechBot()
    {
    	super();
    	
    	
    	
    }
    

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        
        myBTCommunicator = new NewBTCommunicator(myHandler, getResources());
        thisActivity = this;
        rController = new RobotController();
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        messageQueue = new LinkedBlockingQueue<SpeechBot.BotMessage>();
        reusableToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
        setContentView(R.layout.communicator);
        
        speechResults = new ArrayAdapter<String>(this, R.layout.speechresults);
        sProcessor = new SpeechProcessor(InferenceApp.controller);
        Button exitButton, startButton;
        exitButton = (Button)findViewById(R.id.exitAction);
        startButton = (Button)findViewById(R.id.restartAction);
        startButton.setEnabled(false);
        exitButton.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				//destroyBTCommunicator();
				
				thisActivity.finish();
			}
		});
        
        startButton.setOnClickListener(new View.OnClickListener() 
        {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				//executeActionSequence();
				startSpeechProcessing();
			}
		});
        startButton.setEnabled(true);
        TextView sonic = (TextView)findViewById(R.id.dataCenter);
        sonic.setText("No data");
        
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
    	
    	if ((SPEECH_INTENT==requestCode)&&(resultCode == RESULT_OK))
    	{
    		ArrayList<String> results;
    		results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
    		
    		speechResults.clear();

    		try
        	{
    			if (results.size()>0)
        		{
    				SpeechProcessor.SpeechResults[] sResults;
    				sResults = sProcessor.processSpeech(results.get(0));
    				
    				if (sResults!=null)
    				{
    					for (SpeechProcessor.SpeechResults sresult:sResults)
    					{
    						speechResults.add(sresult.functionName);
    					}
    					ListView resultView = (ListView)findViewById(R.id.parsedWords);
        				resultView.setAdapter(speechResults);
    				}
        		}

        	}
        	catch (Exception e)
        	{
        		String o = e.toString();
        	}
    	}
    }
    

    private String getBestPhrase(ArrayList<String> phrase)
    {
    	return null;
    }
    
    
    private void startSpeechProcessing()
    {
    	Intent speechIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
		speechIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
		speechIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, "speak to me!");
		speechIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 5);
		speechIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.ENGLISH);
		startActivityForResult(speechIntent, SPEECH_INTENT);
    }
	
    // 2
    @Override
    protected void onStart() 
    {
        super.onStart();
        
        selectNXT();
        
    }
    


	
    
    
    @Override
    protected Dialog onCreateDialog(int id)
    {
    	switch (id)
    	{
    		case DEVICE_CH0OSER_DIALOG:
    			Dialog dialog = new Dialog(this);
    			dialog.setContentView(R.layout.device_list);
    			return dialog;
    	}
    	return null;
    }
    
    @Override
    protected void onPrepareDialog(int id, Dialog theDialog, Bundle data)
    {
    	switch (id)
    	{
    		case DEVICE_CH0OSER_DIALOG:
    			ListView pairedDeviceList = (ListView)theDialog.findViewById(R.id.paired_devices);
    			BluetoothAdapter  adapter = BluetoothAdapter.getDefaultAdapter();
    	        
    	        Set<BluetoothDevice> pairedDevices = adapter.getBondedDevices();
    			ArrayAdapter<String> deviceList = new ArrayAdapter<String>(this, R.layout.device_name);
    			for (BluetoothDevice device:pairedDevices)
    			{
    				if (device.getName().indexOf("NXT")>=0)
    					deviceList.add(device.getName()+"-"+device.getAddress());
    			}
    			if (deviceList.getCount()>0)
    			{
    				pairedDeviceList.setAdapter(deviceList);
    				pairedDeviceList.setOnItemClickListener(getOnBluetoothDeviceSelected());
    			}
    			else
    			{
    				showToast("No Bluetooth",Toast.LENGTH_SHORT );
    				theDialog.dismiss();
    			}
    	}
    }
    
    private void processRobot()
    {
    	Thread pThread = new Thread()
    	{
    		public void run()
    		{
    			String messageText;
    			Bundle payload = new Bundle();
    			Message message = new Message();
    			try
        		{
        			BotMessage currentMessage=new BotMessage(null,true);
        			messageQueue.put(currentMessage);
        			Hashtable<String,Integer> rawData;
        			RobotController.ActionResponse response;
        			while (true)
        			{
        				currentMessage=messageQueue.take();
        				if (currentMessage.cont==false)
        					break;
        				rawData=getData();
        				message = myHandler.obtainMessage();
        				message.setData(payload);
//        				payload.putString("battery", ""+rawData.get("battery"));
//        				payload.putString("sonic", ""+rawData.get("ultrasonic"));
//        				payload.putString("bumper", ""+rawData.get("switch"));
//        				payload.putInt("message", NewBTCommunicator.UPDATE_CENTER);
//        				myHandler.sendMessage(message);
        				response=rController.updateBotData(rawData, currentMessage.data);
        				myBTCommunicator.updateMotorControl(response.leftPower, response.rightPower);
        				currentMessage=new BotMessage(null,true);
        				Thread.sleep(200);
        				messageQueue.put(currentMessage);
        			}
        			
        		}
        		catch (Exception e)
        		{
        			// TODO: write debug message
        			String exception = e.toString();
        		}
    		}
    		
    	};
    	pThread.start();
    }
    
    private Hashtable<String, Integer> getData()
    {
    	Hashtable<String, Integer> dOut = new Hashtable<String, Integer>();
    	int leftTach, rightTach, ultrasonic, battery, frontCollision;
    	
    	leftTach = myBTCommunicator.getLeftMotorPosition();
    	rightTach = myBTCommunicator.getRightMotorPosition();
    	ultrasonic= myBTCommunicator.getDistanceValue();
    	battery = myBTCommunicator.getBatteryValue();
    	frontCollision = myBTCommunicator.getTouchSensorValue();
    	dOut.put("leftTach", leftTach);
    	dOut.put("rightTach", rightTach);
    	dOut.put("ultrasonic", ultrasonic);
    	dOut.put("battery", battery);
    	dOut.put("switch", frontCollision);
    	
    	return dOut;
    }
    
    private AdapterView.OnItemClickListener getOnBluetoothDeviceSelected()
    {
    	return new AdapterView.OnItemClickListener()
    	{
    		public void onItemClick(AdapterView av, View v, int arg2, long arg3) 
            {

                String info = ((TextView) v).getText().toString();
                String[] name_mac_pair = info.split("\\-");
                selectedMacAddress=name_mac_pair[1];
                myBTCommunicator.onStart(selectedMacAddress);
                dismissDialog(DEVICE_CH0OSER_DIALOG);
            }
    	};
    	
    }
    
    @Override
    public void onResume() {
        super.onResume();
        
    }
    
	
	
    private void executeActionSequence()
    {
    	
    	myBTCommunicator.updateMotorControl(-20, -20);
    	myBTCommunicator.sendSleepMessage(4000);
    	myBTCommunicator.updateMotorControl(20, 20);
    	myBTCommunicator.sendSleepMessage(2000);
    	myBTCommunicator.updateMotorControl(50, -50);
    	myBTCommunicator.sendSleepMessage(8000);
    	myBTCommunicator.updateMotorControl(0, 0);
    	//TextView sonic = (TextView)findViewById(R.id.sonicView);
//    	myBTCommunicator.setUltraSonicDistance();
//    	distance = myBTCommunicator.getDistanceValue();
//        sonic.setText(String.format("%1$s", distance));
        
    }
    
    
	
    @Override
    public void onPause() 
    {
    	
        super.onPause();
    }
    

    @Override
    public void onStop()
    {
    	myBTCommunicator.onStop();
    	try
    	{
    		BotMessage b = new BotMessage("", false);
    		messageQueue.put(b);
    		
    	}
    	catch (Exception e)
    	{
    		
    	}
    	super.onStop();
    }
    
    @Override
    protected void onDestroy() 
    {
        super.onDestroy();
    }





    
   
	
	
    /**
     * Displays a message as a toast
     * @param textToShow the message
     * @param length the length of the toast to display
     */
    private void showToast(String textToShow, int length) {
        reusableToast.setText(textToShow);
        reusableToast.setDuration(length);
        reusableToast.show();
    }

    /**
     * Displays a message as a toast
     * @param resID the ressource ID to display
     * @param length the length of the toast to display
     */
    private void showToast(int resID, int length) {
        reusableToast.setText(resID);
        reusableToast.setDuration(length);
        reusableToast.show();
    }
    


    private int byteToInt(byte byteValue) {
        int intValue = (byteValue & (byte) 0x7f);

        if ((byteValue & (byte) 0x80) != 0)
            intValue |= 0x80;

        return intValue;
    }

    // 2.1
    // 
    void selectNXT() 
    {
        showDialog(DEVICE_CH0OSER_DIALOG);
    }



    
    
    /**
     * Initialization of the motor commands for the different robot types.
     */
    // 1.1




	


}
